package ru.pb.game.idfactory;

import gnu.trove.impl.sync.TSynchronizedIntList;
import gnu.trove.list.array.TIntArrayList;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Arrays;

import org.apache.log4j.Logger;

import ru.pb.global.dbc.DatabaseFactory;
import ru.pb.global.dbc.DatabaseUtils;

/**
 * @author Felixx
 */
public abstract class IdFactory
{
	private static Logger _log = Logger.getLogger(IdFactory.class.getName());

	public static final int FIRST_OID = 0x10000000;
	public static final int LAST_OID = 0x7FFFFFFF;
	public static final int FREE_OBJECT_ID_SIZE = LAST_OID - FIRST_OID;

	public static final String[][] EXTRACT_OBJ_ID_TABLES = { {"players", "player_id"}, {"items", "object_id"}, {"clan_data", "clan_id"}};

	protected static final IdFactory _instance = new BitSetIDFactory();

	protected IdFactory()
	{
		setAllCharacterOffline();
		cleanUpDB();
	}

	private void setAllCharacterOffline()
	{
		Connection conn = null;
		Statement stmt = null;
		try
		{
			conn = DatabaseFactory.getInstance().newConnection();
			stmt = conn.createStatement();
			stmt.executeUpdate("UPDATE players SET online = 0");
			_log.info("Clear players online status.");
		}
		catch(SQLException e)
		{
			e.printStackTrace();
		}
		finally
		{
			DatabaseUtils.closeDatabaseCS(conn, stmt);
		}
	}

	/**
	 * Cleans up Database
	 */
	private void cleanUpDB()
	{
		Connection conn = null;
		Statement stmt = null;
		try
		{
			int cleanCount = 0;
			int curCount;

			conn = DatabaseFactory.getInstance().newConnection();
			stmt = conn.createStatement();

			// if((curCount =
			// stmt.executeUpdate("DELETE FROM player_friends WHERE player_friends.player_id NOT IN (SELECT player_id FROM players) OR player_friends.friend_id NOT IN (SELECT player_id FROM players);"))
			// > 0)
			// {
			// cleanCount += curCount;
			// _log.info("Cleaned " + curCount + " elements from table player_friends.");
			// }
			// if((curCount =
			// stmt.executeUpdate("DELETE FROM player_blocklist WHERE player_blocklist.player_id NOT IN (SELECT player_id FROM players) OR player_blocklist.target_id NOT IN (SELECT player_id FROM players);"))
			// > 0)
			// {
			// cleanCount += curCount;
			// _log.info("Cleaned " + curCount + " elements from table player_friends.");
			// }
			// if((curCount = stmt.executeUpdate("DELETE FROM player_macroses WHERE player_macroses.player_id NOT IN (SELECT player_id FROM players);")) > 0)
			// {
			// cleanCount += curCount;
			// _log.info("Cleaned " + curCount + " elements from table player_macroses.");
			// }
			// if((curCount = stmt.executeUpdate("DELETE FROM player_quests WHERE player_quests.player_id NOT IN (SELECT player_id FROM players);")) > 0)
			// {
			// cleanCount += curCount;
			// _log.info("Cleaned " + curCount + " elements from table player_quests.");
			// }
			// if(GameServerConfig.HARD_CLEANUP_ON_START_UP && (curCount = stmt.executeUpdate("DELETE FROM clan_data WHERE clan_data.leader_id NOT IN (SELECT player_id FROM players);")) > 0)
			// {
			// cleanCount += curCount;
			// _log.info("Cleaned " + curCount + " elements from table clan_data.");
			// }
			// if(GameServerConfig.HARD_CLEANUP_ON_START_UP && (curCount =
			// stmt.executeUpdate("DELETE FROM items WHERE owner_id NOT IN (SELECT obj_Id FROM characters) AND owner_id NOT IN (SELECT clan_id FROM clan_data) AND owner_id NOT IN (SELECT objId FROM pets) AND owner_id NOT IN (SELECT id FROM npc);"))
			// > 0)
			// {
			// cleanCount += curCount;
			// _log.info("Cleaned " + curCount + " elements from table items.");
			// }

			// if((curCount =
			// stmt.executeUpdate("UPDATE players SET clanid=0,pledge_type=0,pledge_rank=0,lvl_joined_academy=0,apprentice=0 WHERE clanid!=0 AND clanid NOT IN (SELECT clan_id FROM clan_data);")) > 0)
			// {
			// _log.info("Updated " + curCount + " elements from table players.");
			// }

			_log.info("Total cleaned " + cleanCount + " elements from database.");
		}
		catch(SQLException e)
		{
			e.printStackTrace();
		}
		finally
		{
			DatabaseUtils.closeDatabaseCS(conn, stmt);
		}
	}

	protected int[] extractUsedObjectIDTable() throws SQLException
	{
		TSynchronizedIntList objectIds = new TSynchronizedIntList(new TIntArrayList());

		Connection con = null;
		Statement st = null;
		ResultSet rs = null;
		try
		{
			con = DatabaseFactory.getInstance().newConnection();
			st = con.createStatement();
			for(String[] table : EXTRACT_OBJ_ID_TABLES)
			{
				rs = st.executeQuery("SELECT " + table[1] + " FROM " + table[0]);
				int size = objectIds.size();
				while(rs.next())
					objectIds.add(rs.getInt(1));

				DatabaseUtils.closeResultSet(rs);

				size = objectIds.size() - size;
				if(size > 0)
					_log.info("IdFactory: Extracted " + size + " used id's from " + table[0]);
			}
		}
		finally
		{
			DatabaseUtils.closeDatabaseCSR(con, st, rs);
		}

		int[] extracted = objectIds.toArray();

		Arrays.sort(extracted);

		_log.info("IdFactory: Extracted total " + extracted.length + " used id's.");

		return extracted;
	}

	public static IdFactory getInstance()
	{
		return _instance;
	}

	public abstract int getNextId();

	public abstract void releaseId(int id);

	public abstract int size();
}